using System;
using System.Text;
using System.Diagnostics;
using System.Collections.Generic;
using System.Security.Cryptography;

namespace Microsoft.Cryptography.Diagnostics
{
    /// <summary>
    /// Helper class for tracing
    /// </summary>
    public class LogProvider
    {
        /// <summary>
        /// Property for the delegate, which is used to log messages
        /// </summary>
        /// <param name="priority">priority for the message, which should be logged</param>
        /// <param name="sources">log sources for the message, which should be logged</param>
        /// <param name="message">message text, which should be logged</param>
        /// <param name="parameters">parameters for the message, which should be logged</param>
        public delegate void LogMessageDelegate(LogPriorities priority,
            LogSources[] sources,
            string message,
            params object[] parameters);

        private static LogMessageDelegate _logMessageDelegate;

        /// <summary>
        /// Property for the delegate, which is used to log messages
        /// </summary>
        public static LogMessageDelegate LogMessageDel
        {
            get
            {
                if (_logMessageDelegate == null)
                {
                    object logMessage = CryptoConfig.CreateFromName("Microsoft.Cryptography.Diagnostics.ILogMessage");
                    if (logMessage != null &&
                        logMessage is ILogMessage)
                    {
                        _logMessageDelegate = new LogMessageDelegate(((ILogMessage)logMessage).LogMessage);
                    }

                    if (_logMessageDelegate == null)
                    {
                        throw new ArgumentNullException("_logMessage");
                    }
                }

                return _logMessageDelegate;
            }

            set
            {
                if (value == null)
                {
                    throw new ArgumentNullException("_logMessage", "The value for '_logMessage' must not be NULL!");
                }

                _logMessageDelegate = value;
            }
        }

        static LogProvider()
        {
            object logMessage = CryptoConfig.CreateFromName("Microsoft.Cryptography.Diagnostics.ILogMessage");
            if (logMessage != null &&
                logMessage is ILogMessage)
            {
                _logMessageDelegate = new LogMessageDelegate(((ILogMessage)logMessage).LogMessage);
            }
        }

        /// <summary>
        /// List of possible TraceSources
        /// </summary>
        public enum LogSources
        {
            /// <summary>
            /// TraceSource for error and exceptions
            /// </summary>
            CRYPTO_Errors = 1,
            /// <summary>
            /// TraceSource for basic components like BERReader and RSA-calculation
            /// </summary>
            CRYPTO_Basis = 2,
            /// <summary>
            /// TraceSource for X509-Certificate and CRL components
            /// </summary>
            CRYPTO_X509 = 10,
            /// <summary>
            /// TraceSource for PKCS#7-components
            /// </summary>
            CRYPTO_PKCS7 = 20,
            /// <summary>
            /// TraceSource for OCSP-components
            /// </summary>
            CRYPTO_OCSP = 30,
            /// <summary>
            /// TraceSource for XML-Signature components
            /// </summary>
            CRYPTO_XmlSig = 40,
            /// <summary>
            /// TraceSource for Windows Communication Foundation components
            /// </summary>
            CRYPTO_WCF = 50,
        }

        /// <summary>
        /// Liste of possible priorities
        /// </summary>
        public enum LogPriorities
        {
            /// <summary>
            /// Trace-Entries, which are relevant for developers to analyze errors only.
            /// The output for these enries should be disabled in production environment.
            /// </summary>
            Debug = 1,
            /// <summary>
            /// Trace-entries, which belong to successfull operations
            /// The output for these enries should be disabled in production environment.
            /// </summary>
            Info = 2,
            /// <summary>
            /// Trace-entries, which belong to operations with unexpected condtions
            /// NO EXCEPTIONS!!!
            /// The output for these enries should be enabled in production environment.
            /// </summary>
            Warning = 3,
            /// <summary>
            /// Trace-entries, which are relevant for adminsitartors (especially Exceptions)
            /// These messages are always traced in the Trace-source "CRYPTO_Errors", too.
            /// The output for these enries should be enabled in production environment.
            /// </summary>
            Error = 4
        }

        /// <summary>
        /// Method to trace an exception
        /// </summary>
        /// <param name="source">Name of the Trace-source</param>
        /// <param name="message">Message, which should be traced</param>
        /// <param name="parameters">Additional parameters</param>
        private static void LogException(LogSources source,
            string message,
            params object[] parameters)
        {
           LogMessage(LogPriorities.Error,
                source,
                message,
                parameters);
        }


        /// <summary>
        /// Method to trace an exception to the 'CRYPTO_Errors' trace-source only
        /// </summary>
        /// <param name="message">Message, which should be traced</param>
        /// <param name="parameters">Additional parameters</param>
        public static void LogException(string message,
            params object[] parameters)
        {
            LogMessage(LogPriorities.Error,
                LogSources.CRYPTO_Errors,
                message,
                parameters);
        }

        /// <summary>
        /// Method to trace an exception to the 'CRYPTO_Errors' trace-source only
        /// </summary>
        /// <param name="error">Exception, which should be traced</param>
        public static void LogException(Exception error)
        {
            LogException(error.ToString(), null);
        }

        /// <summary>
        /// Method to trace an exception to the 'CRYPTO_Errors' trace-source only
        /// </summary>
        /// <param name="error">Exception, which should be traced</param>
        /// <param name="source">Name of the Trace-source</param>
        public static void LogException(Exception error,
            LogSources source)
        {
            LogException(source,
                error.ToString(),
                null);
        }

        /// <summary>
        /// Method to trace messages
        /// </summary>
        /// <param name="priority">Priority</param>
        /// <param name="sources">Trace-sources, which will be used to trace the message</param>
        /// <param name="message">Message text</param>
        /// <param name="parameters">Additional parameters</param>
        private static void LogMessage(LogPriorities priority,
            LogSources[] sources,
            string message,
            params object[] parameters)
        {

            LogMessageDel(priority, sources, message, parameters);
        }

        /// <summary>
        /// Private constructor to suppress initialization of class-instances
        /// </summary>
        private LogProvider()
        {
        }

        /// <summary>
        /// Method to trace messages
        /// </summary>
        /// <param name="priority">Priority</param>
        /// <param name="source">Trace-source, which will be used to trace the message</param>
        /// <param name="message">Message text</param>
        /// <param name="parameters">Additional parameters</param>
        public static void LogMessage(LogPriorities priority,
            LogSources source,
            string message,
            params object[] parameters)
        {
            LogMessage(priority,
                new LogSources[1] { source },
                message,
                parameters);
        }
    }
}
